package metadatafactroy

import (
	"fmt"
	"gitee.com/ichub/webcli/common/base/baseconfig"
	"gitee.com/ichub/webcli/common/base/baseconsts"
	"gitee.com/ichub/webcli/common/base/baseutils/stringutils"
	"gitee.com/ichub/webcli/common/base/metadata/ichubmetadata"
	"gitee.com/ichub/webcli/common/base/metadata/metadatacontext"
	"gitee.com/ichub/webcli/common/dbcontent"
	"gitee.com/ichub/webcli/common/dbcontent/database"
	"github.com/jinzhu/copier"
	"github.com/sirupsen/logrus"
	"log"
)

/*
@Title    文件名称: db_factroy.go
@Description  描述: 础数据库工厂

@Author  作者: leijianming@163.com  时间(2024-01-31 22:38:21)
@Update  作者: leijianming@163.com  时间(2024-01-31 22:38:21)
*/
type MetadataFactroy struct {
	DbClientDto  *baseconfig.DbClientDto
	funcGetDb    database.FuncGetDb `json:"-"`
	Table        string
	TableComment *string

	Pkey            string
	PkeyType        string
	StringFieldName string `json:"-"`
	StringFieldLen  string `json:"-"`

	mysql    FacatroyMysql   `json:"-"`
	postgres FactroyPostgres `json:"-"`
}

func NewMetadataFactroy() *MetadataFactroy {
	var factry = &MetadataFactroy{}

	factry.funcGetDb = dbcontent.GetDB
	factry.mysql.funcGetDb = factry.funcGetDb
	factry.postgres.funcGetDb = factry.funcGetDb

	return factry
}

func (this *MetadataFactroy) IsMysql() bool {
	return this.DbClientDto.Dbtype == baseconsts.DB_TYPE_MYSQL
}

func (this *MetadataFactroy) findPGTableComment() *ichubmetadata.MetadataTable {
	sql := `SELECT 
		relname as table_name,
		obj_description(oid) as table_comment
		FROM pg_class
		WHERE relkind = 'r' and relname='%s'
	`
	sql = fmt.Sprintf(sql, this.Table)
	var tables ichubmetadata.MetadataTable
	e := this.funcGetDb().Model(&ichubmetadata.MetadataPkInfo{}).Raw(sql).Find(&tables).Error
	if e != nil {
		fmt.Println("***=" + e.Error())

	}
	return &tables
}

func (this *MetadataFactroy) findPGColumnComment() []ichubmetadata.MetadataColumn {
	s := `SELECT 
    c.relname as TableName,
    col_description ( a.attrelid, a.attnum ) AS ColumnComment,
    format_type ( a.atttypid, a.atttypmod ) AS ColumnType,
    a.attname AS ColumnName 
	FROM pg_class AS c,    pg_attribute AS a
	WHERE    a.attrelid = c.oid   AND a.attnum >0
 			and c.relname = '%s'  `
	s = fmt.Sprintf(s, this.Table)
	var cs []ichubmetadata.MetadataColumn
	e := this.funcGetDb().Model(&ichubmetadata.MetadataPkInfo{}).Raw(s).Find(&cs).Error
	if e != nil {
		fmt.Println("***=" + e.Error())
	}

	return cs
}

//dbcontent type->go type

func (this *MetadataFactroy) FindPgPkey(table string) []ichubmetadata.MetadataPkInfo {
	s := `SELECT
	pg_constraint.conname AS pkname,
	pg_attribute.attname AS colname,
	pg_type.typname AS typename
		FROM pg_constraint
		INNER JOIN pg_class ON pg_constraint.conrelid = pg_class.oid
		INNER JOIN pg_attribute ON pg_attribute.attrelid = pg_class.oid
		AND pg_attribute.attnum = pg_constraint.conkey [ 1 ]
		INNER JOIN pg_type ON pg_type.oid = pg_attribute.atttypid
	WHERE	pg_class.relname = '%s'
		AND pg_constraint.contype = 'p' `

	var pkInfos []ichubmetadata.MetadataPkInfo
	s = fmt.Sprintf(s, table)
	logrus.Info("Find PKey: " + s)
	e := this.funcGetDb().Model(&ichubmetadata.MetadataPkInfo{}).Raw(s).Find(&pkInfos).Error
	if e != nil {
		fmt.Println(e.Error())
	}

	log.Println("FindPgPkey: \n" + s)
	if len(pkInfos) == 0 {
		fmt.Println(fmt.Sprintf("pkInfos len=0 无主键（可能表%s不存在！） ", table))
	}
	logrus.Infof("pkInfos: %d", len(pkInfos))
	return pkInfos
}

func (this *MetadataFactroy) FindFields(table string, fieldsName string) *ichubmetadata.MetadataTable {

	var metatable = this.FindMetadata(table)
	metatable.FieldsName = metatable.ToFieldsString()
	if len(fieldsName) != 0 && fieldsName != "*" {
		metatable.FieldsName = fieldsName
	}
	return metatable
}

func (this *MetadataFactroy) iniDb() {
	if this.DbClientDto != nil {
		return
	}
	this.DbClientDto = Cfg.ReadIchubDb()
	metadatacontext.MetadataContextInst.DbType = this.DbClientDto.Dbtype

}
func (this *MetadataFactroy) FindMetadata(table string) *ichubmetadata.MetadataTable {
	var c, found = ichubmetadata.InstMetadataCache.CacheGet(table)
	if found {
		return c
	}
	var DbFindMeta = func() *ichubmetadata.MetadataTable {

		this.iniDb()
		this.Table = table
		var metadataTable = ichubmetadata.NewMetadataTable()
		metadataTable.TableExist = true
		if this.IsMysql() {
			copier.Copy(&this.mysql, this)
			metadataTable.Cols = *this.mysql.FindColumns()

		} else {
			copier.Copy(&this.postgres, this)
			metadataTable.Cols = *this.postgres.FindColumns()
		}
		metadataTable.TableSchema = this.DbClientDto.Dbname
		metadataTable.TableName = table
		metadataTable.TableExist = len(metadataTable.Cols) > 0
		metadataTable.BuildGoFields()
		metadataTable.PkInfo = ichubmetadata.NewMetadataPkInfo()
		if this.IsMysql() {
			metadataTable.PkInfo.TypeName = this.mysql.PkeyType
			metadataTable.PkInfo.PkName = this.mysql.Pkey
		} else {
			metadataTable.PkInfo.TypeName = this.postgres.PkeyType
			metadataTable.PkInfo.PkName = this.postgres.Pkey
		}
		metadataTable.PkInfo.ColName = stringutils.Camel2Case(metadataTable.PkInfo.PkName)

		return metadataTable
	}
	var meta = DbFindMeta()
	meta.Parse2EsIndex()
	//设置cache
	ichubmetadata.InstMetadataCache.CacheSet(table, meta)

	return meta

}

func (this *MetadataFactroy) FindTableComment() {
	if this.IsMysql() {
		tbls := this.FindTables()
		for _, v := range tbls {
			if this.Table == v.TableName {
				this.TableComment = &v.TableComment
				return
			}
		}
	}

	if !this.IsMysql() {
		tc := this.findPGTableComment()
		this.TableComment = &tc.TableComment

	}

}

func (this *MetadataFactroy) FindTables() []ichubmetadata.MetadataTable {
	var tables []ichubmetadata.MetadataTable
	if this.IsMysql() {
		sql := fmt.Sprintf(mysql_findTable, this.DbClientDto.Dbname)
		err := this.funcGetDb().Raw(sql).Find(&tables).Error
		if err != nil {
			panic(err.Error() + " :" + sql)
			return []ichubmetadata.MetadataTable{}
		}
	} else {
		sql := postgres_findTable
		err := this.funcGetDb().Raw(sql).Find(&tables).Error
		if err != nil {
			panic(err.Error() + " :" + sql)

		}
	}
	return tables
}

func (this *MetadataFactroy) FindGoType(fieldType string) (goType string) {

	goType = metadatacontext.MetadataContextInst.FindGoType(fieldType)
	return
}

func (this *MetadataFactroy) FindProtoType(fieldType string) (pbType string) {

	pbType = metadatacontext.MetadataContextInst.FindProtoType(fieldType)
	return
}
